﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

namespace BToolkit.ExeUtils
{
    public class MenuScroll : MonoBehaviour
    {
        public static Texture[] textures;
        public static string[] nameCNs;
        public static string[] nameENs;
        public static string[] bgIDs;
        public static MenuScroll instance;

        public string folder;
        public float radiusX = 1600;
        public float radiusY = -200;
        public float posY = 0;
        public float maxScale = 1f;
        public float minScale = 0.3f;
        public float menuSize = 900;
        public float selectedSize = 80;
        public float borderSize = 10;
        public float tweenSpeed = 0.3f;
        public bool loop;

        /// <summary>
        /// 对外
        /// </summary>
        public Action<int, Menu> onMenuClick;
        /// <summary>
        /// 不对外
        /// </summary>
        internal Action<Menu> _menuSelect;
        internal int total { get; private set; }
        internal float angleSpace;
        List<Menu> queue;
        Menu[] menus;
        bool canDrag;
        float dragDelta, addAngleOffset;
        Vector2 prePos;
        int showCount = 6;
        float addAngle;
        BMath.Line scaleLine;
        BMath.Line alphaLine;
        bool hasInitLayer;
        Menu currBottomPhoto;
        int minIndex, maxIndex;
        Vector2[] touchDownPos;
        bool hadTriggerOnStop;
        public static int currSelectIndex;
        Canvas _canvas;
        Canvas canvas { get { return _canvas ?? (_canvas = GetComponentInParent<Canvas>()); } }

        public static int CurrSelectIndex
        {
            get { return DataStorage.GetInt("CurrSelectIndex", 2); }
            set
            {
                if (currSelectIndex != value)
                {
                    currSelectIndex = value;
                    DataStorage.SetInt("CurrSelectIndex", value);
                }
            }
        }

        public RectTransform rectTransform { get { return transform as RectTransform; } }

        private void OnDestroy()
        {
            _menuSelect -= OnMenuSelect;
        }

        private void Awake()
        {
            instance = this;

            if (textures == null)
            {
                List<string> paths = new List<string>();
                paths.AddRange(MainController.SearchFile(MainController.resPath + "/" + folder, "*.jpg", SearchOption.TopDirectoryOnly));
                paths.AddRange(MainController.SearchFile(MainController.resPath + "/" + folder, "*.png", SearchOption.TopDirectoryOnly));
                textures = new Texture[paths.Count];
                nameCNs = new string[paths.Count];
                nameENs = new string[paths.Count];
                bgIDs = new string[paths.Count];
                for (int i = 0; i < textures.Length; i++)
                {
                    string texPath = paths[i].Replace("\\", "/");
                    textures[i] = MainController.GetTexture(texPath);
                    string txtFilePath = texPath.Replace(".png", ".txt").Replace(".jpg", ".txt");
                    if (File.Exists(txtFilePath))
                    {
                        string txtContent = File.ReadAllText(txtFilePath);
                        string[] lines = txtContent.Split('\n');
                        nameCNs[i] = BUtils.TextSurportSpace(lines[0]);
                        if (lines.Length > 1) nameENs[i] = BUtils.TextSurportSpace(lines[1]);
                        if (lines.Length > 2) bgIDs[i] = lines[2].Trim();
                    }
                }
            }

            currSelectIndex = CurrSelectIndex;
            _menuSelect += OnMenuSelect;

            total = textures.Length;
            if (total > 0)
            {
                if (total < showCount) showCount = total;
                angleSpace = 160 / (float)(showCount - 1);
                if (angleSpace > 35) angleSpace = 35;
                menus = new Menu[showCount];
                int currSelectIndex = CurrSelectIndex;
                if (currSelectIndex > total - 1) currSelectIndex = total - 1;
                int half = Mathf.CeilToInt(showCount * 0.5f);
                Menu _defaultSelectMenu = null;
                for (int i = 0; i < showCount; i++)
                {
                    Menu menu = Instantiate(Resources.Load<Menu>("KouXiangMenu"));
                    menu.transform.SetParent(transform, false);
                    menu.name = "Menu_" + i;
                    menu.angle = i * angleSpace;
                    int photoIndex;
                    if (loop)
                    {
                        photoIndex = currSelectIndex - half + i;
                        while (photoIndex < 0) photoIndex += total;
                        while (photoIndex > total - 1) photoIndex -= total;
                    }
                    else
                    {
                        if (currSelectIndex < showCount)
                        {
                            photoIndex = i;
                        }
                        else if (currSelectIndex > total - showCount)
                        {
                            photoIndex = total - showCount + i;
                        }
                        else
                        {
                            photoIndex = currSelectIndex - showCount + 1 + i;
                        }
                    }
                    menu.Init(this, photoIndex, menuSize);
                    menu.mask.index = i;
                    menu.mask.AddDown(OnPhotoTouchDown);
                    menu.mask.AddUp(OnPhotoTouchUp);
                    menus[i] = menu;
                    if (currSelectIndex == menu.index)
                    {
                        _defaultSelectMenu = menu;
                    }
                }
                minIndex = menus[0].index;
                maxIndex = menus[showCount - 1].index;
                queue = new List<Menu>(menus);
                touchDownPos = new Vector2[showCount];
                RefreshLines();
                RotateToCenter(_defaultSelectMenu);
                _menuSelect?.Invoke(_defaultSelectMenu);
            }
        }

        void Update()
        {
            if (total == 0) return;
            if (Input.GetMouseButtonUp(0))
            {
                canDrag = false;
            }
            if (canDrag)
            {
                Vector2 touchPos = BUtils.ScreenToUGUIPoint(Input.mousePosition, canvas);
                dragDelta = touchPos.x - prePos.x;
                prePos = touchPos;
            }
            else
            {
                dragDelta += (0f - dragDelta) * tweenSpeed;
                if (!loop)
                {
                    if (Mathf.Abs(dragDelta) < 1f)
                    {
                        if (!hadTriggerOnStop)
                        {
                            Menu menu0 = GetMenuByPhotoIndex(0);
                            Menu menuEnd = GetMenuByPhotoIndex(total - 1);
                            if (menu0 && menu0.angle > 90)
                            {
                                RotateToCenter(menu0);
                                _menuSelect?.Invoke(menu0);
                            }
                            else if (menuEnd && menuEnd.angle < 90)
                            {
                                RotateToCenter(menuEnd);
                                _menuSelect?.Invoke(menuEnd);
                            }
                            hadTriggerOnStop = true;
                        }

                    }
                }
            }
            Menu firstPhoto = queue[0];
            firstPhoto.angle += 100 * dragDelta / (radiusX * 2);
            for (int i = 1; i < showCount; i++)
            {
                queue[i].angle = firstPhoto.angle + i * angleSpace;
            }
            for (int i = 0; i < showCount; i++)
            {
                Menu menu = queue[i];
                Vector2 _pos = menu.rectTransform.anchoredPosition;
                _pos.x = -Mathf.Cos(menu.angle * Mathf.Deg2Rad) * radiusX;
                _pos.y = -Mathf.Sin(menu.angle * Mathf.Deg2Rad) * radiusY;
                menu.rectTransform.anchoredPosition = _pos;

                float angleDis = Mathf.Abs(menu.angle - 90);
                //尺寸
                float scale;
                scale = scaleLine.k * angleDis + scaleLine.b;
                menu.SetScale(scale);
                //透明度
                if (angleDis < 50)
                {
                    menu.SetAlpha(1);
                }
                else
                {
                    float alpha = alphaLine.k * angleDis + alphaLine.b;
                    menu.SetAlpha(alpha);
                }
            }
            for (int i = 0; i < showCount; i++)
            {
                Menu menu = queue[i];
                if (dragDelta < 0 || addAngleOffset < 0)
                {
                    //往左滑动时
                    if (menu.angle < -angleSpace * 0.5f)
                    {
                        bool canChangePos = loop ? true : maxIndex < total - 1;
                        if (canChangePos)
                        {
                            menu.angle = 180 + angleSpace * 0.5f;
                            menu.rectTransform.SetAsFirstSibling();
                            //从列表首位移到末尾
                            queue.Remove(menu);
                            queue.Add(menu);
                            minIndex++;
                            maxIndex++;
                            if (minIndex > total - 1) minIndex -= total;
                            if (maxIndex > total - 1) maxIndex -= total;
                            menu.SetIndex(maxIndex);
                            break;
                        }
                    }
                }
                if (dragDelta > 0 || addAngleOffset > 0)
                {
                    //往右滑动时
                    if (menu.angle > 180 + angleSpace * 0.5f)
                    {
                        bool canChangePos = loop ? true : minIndex > 0;
                        if (canChangePos)
                        {
                            menu.angle = -angleSpace * 0.5f;
                            menu.rectTransform.SetAsFirstSibling();
                            //从列末尾位移到表首
                            List<Menu> temp = new List<Menu>(queue);
                            queue[0] = temp[showCount - 1];
                            for (int j = 1; j < showCount; j++)
                            {
                                queue[j] = temp[j - 1];
                            }
                            minIndex--;
                            maxIndex--;
                            if (minIndex < 0) minIndex += total;
                            if (maxIndex < 0) maxIndex += total;
                            menu.SetIndex(minIndex);
                            break;
                        }
                    }
                }
            }
            if (!hasInitLayer)
            {
                int mindleIndex = Mathf.CeilToInt(showCount * 0.5f);
                for (int i = mindleIndex; i < showCount; i++)
                {
                    queue[i].rectTransform.SetAsFirstSibling();
                }
                hasInitLayer = true;
            }

            //实时监听Y轴最低的照片有没有替换，有责刷新层级
            Menu bottomPhoto = GetBottomMenu();
            if (currBottomPhoto != bottomPhoto)
            {
                currBottomPhoto = bottomPhoto;
                currBottomPhoto.rectTransform.SetAsLastSibling();
            }
        }

        void OnMenuSelect(Menu menu)
        {
            CurrSelectIndex = menu.index;
        }

        void RefreshLines()
        {
            //按角度列线性方程
            scaleLine = new BMath.Line(0, maxScale, 90, minScale);
            alphaLine = new BMath.Line(50, 1, 90, 0);
            rectTransform.anchoredPosition = new Vector2(0, posY);
        }

        //自动转到中心
        public void RotateToCenter(Menu menu)
        {
            canDrag = false;
            dragDelta = 0;
            addAngle = 90 - menu.angle;
            StartCoroutine(DoRotateToCenter());
        }
        IEnumerator DoRotateToCenter()
        {
            while (addAngle != 0)
            {
                Menu firstPhoto = queue[0];
                float origin = addAngle;
                addAngle += (0 - addAngle) * tweenSpeed;
                addAngleOffset = origin - addAngle;
                firstPhoto.angle += addAngleOffset;
                if (Mathf.Abs(addAngle) < 1f)
                {
                    addAngle = 0;
                    addAngleOffset = 0;
                }
                yield return new WaitForEndOfFrame();
            }
        }

        void OnPhotoTouchDown(int index)
        {
            prePos = BUtils.ScreenToUGUIPoint(Input.mousePosition, canvas);
            touchDownPos[index] = prePos;
            canDrag = true;
            hadTriggerOnStop = false;
        }

        void OnPhotoTouchUp(int index)
        {
            Vector2 pos = BUtils.ScreenToUGUIPoint(Input.mousePosition, canvas);
            if (Vector2.Distance(touchDownPos[index], pos) < 50)
            {
                Menu menu = menus[index];
                RotateToCenter(menu);
                _menuSelect?.Invoke(menu);
                onMenuClick?.Invoke(index, menu);
            }
        }

        Menu GetBottomMenu()
        {
            Menu _bottomPhoto = null;
            float minY = float.MaxValue;
            for (int i = 0; i < showCount; i++)
            {
                Menu menu = queue[i];
                if (menu.rectTransform.anchoredPosition.y - menu.size * 0.5f < minY)
                {
                    _bottomPhoto = menu;
                    minY = menu.rectTransform.anchoredPosition.y;
                }
            }
            return _bottomPhoto;
        }

        Menu GetMenuByPhotoIndex(int index)
        {
            for (int i = 0; i < showCount; i++)
            {
                Menu menu = queue[i];
                if (menu.index == index)
                {
                    return menu;
                }
            }
            return null;
        }

    }
}